#include "priv/atkconnmanager.hxx"
#include "priv/auconnmanager.hxx"
#include "priv/atkconn.hxx"
#include <tcpserversocket.hxx>
#include <tcpsocket.hxx>
#include <socketexception.hxx>
#include <tcpbridgenet.hxx>
#include <myfilelog.hxx>
#include <loghelper.hxx>
#include <util.hxx>
#include <cstring>
#include <iostream>
#include <macro.hxx>

using namespace std;
AtkConnManager::AtkConnManager(tUInt32 atkport, FileLog *filelog)
: Thread(),
    _atkport(atkport),
    _atkconn_map(),
    _atkconn_map_mutex(),
    _is_shutdown_requested(false),
    _filelog(filelog)
{
}

AtkConnManager::~AtkConnManager()
{
}

void AtkConnManager::Send(const tBridgeMsg* bmsg)
{
    APPENDMSGEX("AtkConnManager broadcasting message to attackers...", 0);

    tBridgeMsg* dup;

    Util::Lock lock(&_atkconn_map_mutex);
    
    tAtkConnMap::iterator iter = _atkconn_map.begin();
    while (iter != _atkconn_map.end())
    {
        APPENDMSGEX("Sending message to attacker "<<iter->second->PeerHostAddress()<<":"<<iter->second->PeerHostPort(), 0);

        dup = DupBMsg(bmsg);
        iter->second->SendMsg(dup);

        ++iter;
    }
}

void AtkConnManager::ClearConns(bool last)
{
    Util::Lock lock(&_atkconn_map_mutex);
    
    int counter = 0;
    AtkConn* atkconn;
    tAtkConnMap::iterator iter = _atkconn_map.begin();
    while (iter != _atkconn_map.end())
    {
        atkconn = iter->second;
        
        if (last)
        {
            atkconn->Shutdown();
            atkconn->Join();
        }
        
        if (atkconn->State() == Thread::FINISHED_STATE)
        {
            atkconn->Join();
            delete atkconn;
            atkconn = 0;

            _atkconn_map.erase(iter++);
            ++counter;
        }
        else
            ++iter;
    }

    if (counter)
        APPENDMSGEX("AtkConnManager "<<counter<<" zombie AtkConn(s) cleared, now "<<_atkconn_map.size()<<" alive.", 3);
}

void AtkConnManager::Shutdown()
{
    _is_shutdown_requested=true;
}

void AtkConnManager::GetCjhRoot(string& cjhroot, string& clxh) const
{
    const string CjhRoot = "LFV3A28K2C3";
    const string Clxh = "1997CC";
    const int CjhLength = 13;

	static int trialIndex = 0;
    static Mutex trialIndexInUse;

    Util::Lock lock(&trialIndexInUse);
	
    stringstream ss;
	ss<<CjhRoot<<(trialIndex++);

	while (string(ss.str()).length() < CjhLength)
		ss<<"0";
    
    cjhroot = ss.str();
    clxh = Clxh;
}

void AtkConnManager::Execute(Thread::Arg arg)
{
    TcpServerSocket* listening_socket;
    TcpSocket* new_socket;

    tUInt32 counter=0;

    APPENDMSGEX("AtkConnManager started.", 3);

    listening_socket = new TcpServerSocket(_atkport);

    while(!_is_shutdown_requested)
    {
        //Since it's time consuming, we do it every 250 loops
        counter++;
        if (counter==250)
        {
            counter=0;
            ClearConns();
        }

        if (0 >= listening_socket->IsReadyForRead(100))
        {
            //Sleep 1 seconds if no connection requested
            Yield();
            continue;
        }

        //try to establish a new connection for a new atk handler.
        try
        {
            new_socket=listening_socket->Accept();
        }
        catch(SocketException &e)
        {
            LOGERROR("AtkConnManager: Failed to instanciate socket for new atk handler: "<<e.Description()<<".");
            continue;
        }


        // create and insert the new conn.
        {
            Util::Lock lock(&_atkconn_map_mutex);
            AtkConn* atkconn = new AtkConn(this, new_socket, _filelog);
            atkconn->Start();

            _atkconn_map.insert(make_pair(atkconn->Id(), atkconn));
            
            APPENDMSGEX("AtkConnManager: Accepted connection from "<<new_socket->PeerHostAddress()<<":"<<new_socket->PeerHostPort()<<", now #"<<_atkconn_map.size()<<" alive.", 3);
        }

    } // while(!_is_shutdown_requested)

    if (listening_socket)
    {
        delete listening_socket;
        listening_socket=0;
    }

    ClearConns();

    APPENDMSGEX("AtkConnManager shutdowned", 3);
}

